#include "../include/winapi.h"
#include "../include/buffer.h"
#include "../include/state.h"
#include "../include/macro_disk.h"
#include "../include/macro_str.h"
#include "../include/macro_report.h"
#include "../include/exception.h"

void WinAPI::getWindowsFsIndex(uint16* root_path,STORAGE_DEVICE_NUMBER& output)
{
	uint16 path[]={BACKSLASH,BACKSLASH,DOT,BACKSLASH,*root_path,COLON,0};
	DWORD readed;
	HANDLE hdrive=CreateFile((LPCWSTR)path,
		GENERIC_READ,
		FILE_SHARE_READ|FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	DeviceIoControl(hdrive,
		IOCTL_STORAGE_GET_DEVICE_NUMBER,
		NULL,
		0,
		&output,
		sizeof(output),
		&readed,
		NULL);
}
uint32 WinAPI::getWindowsFsCount()
{
	uint32 win_fs_count=0;
	DWORD bitmask=0;
	DWORD tmp=1;
	bitmask=GetLogicalDrives();
	for(uint32 i=0;i<sizeof(DWORD)*8;i++)
	{
		if((tmp&bitmask)==tmp)
			win_fs_count++;
		tmp<<=1;
	}
	return win_fs_count;
}
Buffer WinAPI::getWindowsFsName(uint16* root_path)
{
	uint32 name_buffer_size=64;
	uint16* name_buffer=new uint16[name_buffer_size];
	uint32 type_buffer_size=64;
	uint16* type_buffer=new uint16[type_buffer_size];
	GetVolumeInformation((LPCWSTR)root_path,(LPWSTR)name_buffer,name_buffer_size,NULL,NULL,NULL,(LPWSTR)type_buffer,type_buffer_size);
	uint32 i=0;
	while(name_buffer[i]!=0&&i<64){i++;}
	if(i>=64) i=0;
	name_buffer[i]=SLASH;
	name_buffer[i+1]=*root_path;
	name_buffer[i+2]=SLASH;
	name_buffer[i+3]=(uint16)(GetDriveType((LPCWSTR)root_path)+0x30);
	name_buffer[i+4]=SLASH;
	name_buffer[i+5]=0;
	return Buffer((uint8*)name_buffer,name_buffer_size*2);
}
void WinAPI::creatDir(const wint16* name)
{
	if(!CreateDirectory(name,NULL))
	{
		uint32 error_code=GetLastError();
		if(error_code==ERROR_ALREADY_EXISTS&&!State::state().isOverwrite())
		{
			State::state().occurCopyError(ERROR_TYPE_CREATEFILE,error_code);
			if(!State::state().isOverwrite())
				throw ExpWinAPI(ERROR_TYPE_CREATEFILE,error_code); 
		}
		else if(error_code!=ERROR_ALREADY_EXISTS)
			throw ExpWinAPI(ERROR_TYPE_CREATEFILE,error_code);
	}
}
HANDLE WinAPI::createFile(const wint16* name)
{
	DWORD style=CREATE_NEW;
	if(State::state().isOverwrite()) 
		style=CREATE_ALWAYS;
TRY_CREATE_AGAIN:
	HANDLE hfile=CreateFile(name,
							GENERIC_WRITE,
							FILE_SHARE_READ|FILE_SHARE_WRITE,
							NULL,
							style,
							FILE_ATTRIBUTE_NORMAL,
							NULL);
	if(hfile==INVALID_HANDLE_VALUE)
	{
		uint32 error_code=GetLastError();
		if(error_code==ERROR_FILE_EXISTS)
		{
			State::state().occurCopyError(ERROR_TYPE_CREATEFILE,error_code);
			if(!State::state().isOverwrite())
				throw ExpWinAPI(ERROR_TYPE_CREATEFILE,error_code);
			else
			{
				style=CREATE_ALWAYS;
				goto TRY_CREATE_AGAIN;
			}
		}
		else if(error_code!=ERROR_FILE_EXISTS)
			throw ExpWinAPI(ERROR_TYPE_CREATEFILE,error_code);
	}
	return hfile;
}
void WinAPI::readData(HANDLE disk_handle,uint32 sector_ptr,uint32 number_of_byte,Buffer& buffer)
{
	LARGE_INTEGER ptr;
	DWORD number_of_read=0;
	ptr.LowPart=sector_ptr<<SECTOR_SIZE_POWR1;
	ptr.HighPart=sector_ptr>>SECTOR_SIZE_POWR2;
	if(!SetFilePointerEx(disk_handle,ptr, NULL, FILE_BEGIN))
		throw ExpWinAPI(ERROR_TYPE_UNKNOWN,GetLastError());
TRY_READ_AGAIN:
	if(!ReadFile(disk_handle,buffer.data(),number_of_byte,&number_of_read,NULL))
	{
		uint32 error_code=GetLastError();
		if(State::state().isHaveWatch())
		{
			State::state().occurCopyError(ERROR_TYPE_READFILE,error_code);
			switch(State::state().isRetry())
			{
			case NOT_RETRY_AND_DEL_FILE:
				throw ExpWinAPI(ERROR_TYPE_READFILE,error_code,true);
				break;
			case NOT_RETRY:
				throw ExpWinAPI(ERROR_TYPE_READFILE,error_code,false);
				break;
			case RETRY:
				goto TRY_READ_AGAIN; 
			default:;
			}
		}
		else
			throw ExpWinAPI(ERROR_TYPE_READFILE,error_code,false);
	}
}
void WinAPI::writeData(HANDLE file_handle,Buffer& data,uint32 number_of_byte)
{
	DWORD number_of_write_byte=0;
TRY_COPY_AGAIN:
	if(!WriteFile(file_handle,data.data(),number_of_byte,&number_of_write_byte,NULL))
	{
		uint32 error_code=GetLastError();
		State::state().occurCopyError(ERROR_TYPE_WRITEFILE,error_code);
		switch(State::state().isRetry())
		{
		case NOT_RETRY_AND_DEL_FILE:
			throw ExpWinAPI(ERROR_TYPE_WRITEFILE,error_code,true);
			break;
		case NOT_RETRY:
			throw ExpWinAPI(ERROR_TYPE_WRITEFILE,error_code,false);
			break;
		case RETRY:
			goto TRY_COPY_AGAIN;
		default:;
		}
	}
}

